<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket聊天室</title>
    <style>
        /* 全局样式 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Microsoft YaHei', sans-serif;
            background-color: #f0f2f5;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            padding: 20px;
        }

        /* 聊天室容器 */
        .chat-container {
            width: 100%;
            max-width: 800px;
            height: 80vh;
            background: white;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        /* 头部 */
        .chat-header {
            padding: 15px 20px;
            background: #1890ff;
            color: white;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .chat-title {
            font-size: 18px;
            font-weight: bold;
        }

        .online-count {
            font-size: 14px;
            background: rgba(255, 255, 255, 0.2);
            padding: 4px 8px;
            border-radius: 12px;
        }

        /* 消息区域 */
        .messages-container {
            flex: 1;
            padding: 20px;
            overflow-y: auto;
            background: #f7f9fa;
        }

        .message {
            margin-bottom: 15px;
            display: flex;
            flex-direction: column;
            max-width: 80%;
        }

        .message.sent {
            align-self: flex-end;
        }

        .message-content {
            padding: 10px 15px;
            border-radius: 12px;
            background: white;
            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
            word-wrap: break-word;
        }

        .message.sent .message-content {
            background: #1890ff;
            color: white;
        }

        .message-info {
            font-size: 12px;
            color: #999;
            margin-top: 4px;
        }

        /* 输入区域 */
        .input-container {
            padding: 15px;
            background: white;
            border-top: 1px solid #eee;
            display: flex;
            gap: 10px;
        }

        #messageInput {
            flex: 1;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 6px;
            outline: none;
            font-size: 14px;
            transition: border-color 0.3s;
        }

        #messageInput:focus {
            border-color: #1890ff;
        }

        #sendButton {
            padding: 10px 20px;
            background: #1890ff;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        #sendButton:hover {
            background: #40a9ff;
        }

        #sendButton:active {
            background: #096dd9;
        }

        /* 滚动条样式 */
        ::-webkit-scrollbar {
            width: 6px;
        }

        ::-webkit-scrollbar-track {
            background: #f1f1f1;
        }

        ::-webkit-scrollbar-thumb {
            background: #c1c1c1;
            border-radius: 3px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: #a8a8a8;
        }

        /* 模态框样式 */
        #loginModal {
            position: fixed;
            left: 0;
            top: 0;
            width: 100vw;
            height: 100vh;
            background: rgba(0, 0, 0, 0.3);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 10;
        }

        #loginModal > div {
            background: #fff;
            padding: 32px 24px;
            border-radius: 10px;
            box-shadow: 0 2px 12px #0001;
            min-width: 320px;
        }

        #loginModal h2 {
            margin-bottom: 16px;
        }

        #loginModal input {
            width: 100%;
            padding: 8px 12px;
            border-radius: 6px;
            border: 1px solid #ddd;
            margin-bottom: 12px;
        }

        #loginModal button {
            width: 100%;
            padding: 10px 0;
            background: #1890ff;
            color: #fff;
            border: none;
            border-radius: 6px;
            font-size: 16px;
        }

        /* 节点列表样式 */
        #peerList {
            list-style: none;
            padding: 0 0 10px 0;
            margin: 0;
        }

        #peerList li {
            cursor: pointer;
            padding: 8px 12px;
            border-bottom: 1px solid #eee;
        }

        #peerList li:hover {
            background: #f5f5f5;
        }
    </style>
</head>
<body>
    <div id="loginModal">
        <div>
            <h2>P2P节点注册</h2>
            <div>
                <input id="usernameInput" type="text" placeholder="用户名">
            </div>
            <div>
                <input id="groupInput" type="text" placeholder="组名">
            </div>
            <button id="loginBtn">注册并扫描节点</button>
        </div>
    </div>
    <div class="chat-container" style="display:none;" id="mainApp">
        <div class="chat-header">
            <div class="chat-title">P2P聊天室</div>
            <div class="online-count">在线节点: <span id="peerCount">0</span></div>
        </div>
        <div style="display:flex;flex:1;overflow:hidden;">
            <div style="width:220px;background:#f7f9fa;border-right:1px solid #eee;overflow-y:auto;">
                <div style="padding:10px 12px;font-weight:bold;">节点列表</div>
                <ul id="peerList"></ul>
            </div>
            <div style="flex:1;display:flex;flex-direction:column;">
                <div class="messages-container" id="messagesContainer"></div>
                <div class="input-container">
                    <input type="text" id="messageInput" placeholder="请输入消息..." maxlength="500">
                    <button id="sendButton">发送</button>
                </div>
            </div>
        </div>
    </div>

    <script>
        // --- P2P 前端核心逻辑 ---
        let ws = null;
        let myInfo = { username: '', group: '' };
        let peerList = [];
        let selectedPeer = null;
        function peerKey(peer) {
            return peer.username + '|' + peer.group;
        }

        let chatHistory = {}; // { peerKey: [ {username, content, self, time} ] }
        let unreadPeers = new Set();

        // 登录注册
        const loginModal = document.getElementById('loginModal');
        const mainApp = document.getElementById('mainApp');
        const usernameInput = document.getElementById('usernameInput');
        const groupInput = document.getElementById('groupInput');
        const loginBtn = document.getElementById('loginBtn');
        const peerListEl = document.getElementById('peerList');
        const peerCountEl = document.getElementById('peerCount');
        const messagesContainer = document.getElementById('messagesContainer');
        const messageInput = document.getElementById('messageInput');
        const sendButton = document.getElementById('sendButton');

        loginBtn.onclick = function() {
            const username = usernameInput.value.trim();
            const group = groupInput.value.trim();
            if (!username || !group) {
                alert('请输入用户名和组名');
                return;
            }
            myInfo.username = username;
            myInfo.group = group;
            loginModal.style.display = 'none';
            mainApp.style.display = '';
            connectWS();
        };

        function connectWS() {
            ws = new WebSocket('ws://' + window.location.hostname + ':3000');
            ws.onopen = function() {
                ws.send(JSON.stringify({ type: 'register', username: myInfo.username, group: myInfo.group }));
            };
            ws.onmessage = handleWSMsg;
            ws.onclose = function() {
                appendSystemMessage('与服务器断开连接');
            };
        }

        function updatePeerList(peers) {
            peerList = peers.filter(p => !(p.username === myInfo.username && p.group === myInfo.group));
            peerListEl.innerHTML = '';
            peerList.forEach(peer => {
                const key = peerKey(peer);
                const li = document.createElement('li');
                li.textContent = `${peer.username}（${peer.group}）`;
                li.style.cursor = 'pointer';
                li.style.padding = '8px 12px';
                li.style.borderBottom = '1px solid #eee';
                if (selectedPeer && peerKey(selectedPeer) === key) {
                    li.style.background = '#e6f7ff';
                }
                if (unreadPeers.has(key)) {
                    li.style.background = '#fffbe6'; // 高亮未读
                }
                li.onclick = () => {
                    selectedPeer = peer;
                    unreadPeers.delete(key);
                    updatePeerList(peerList);
                    renderChatWithPeer(key);
                };
                peerListEl.appendChild(li);
            });
            peerCountEl.textContent = peerList.length;
        }

        // 文件传输相关
        let fileToSend = null;
        let fileReader = null;
        let fileSendIndex = 0;
        let fileSendChunks = [];
        let fileSendTarget = null;
        let fileReceiveBuffer = {};

        // 统一 WebSocket 消息处理，支持 fileChunk
        function handleWSMsg(event) {
            const data = JSON.parse(event.data);
            if (data.type === 'peerList') {
                updatePeerList(data.peers);
            } else if (data.type === 'message') {
                if (!(data.fromUsername === myInfo.username && data.fromGroup === myInfo.group)) {
                    const key = data.fromUsername + '|' + data.fromGroup;
                    saveChat(key, { username: data.fromUsername, group: data.fromGroup, content: data.content, self: false, time: new Date() });
                    if (!selectedPeer || peerKey(selectedPeer) !== key) {
                        unreadPeers.add(key);
                        updatePeerList(peerList);
                    } else {
                        renderChatWithPeer(key);
                    }
                }
            } else if (data.type === 'fileChunk') {
                // 用发送方用户名+组名+文件名唯一标识
                const key = data.fromUsername + '|' + data.fromGroup + '_' + data.fileName;
                if (!fileReceiveBuffer[key]) {
                    fileReceiveBuffer[key] = { chunks: [], total: data.totalChunks, size: data.fileSize };
                }
                fileReceiveBuffer[key].chunks[data.chunkIndex] = data.data;
                // 如果全部收到
                if (fileReceiveBuffer[key].chunks.filter(Boolean).length === data.totalChunks) {
                    const base64 = fileReceiveBuffer[key].chunks.join('');
                    const link = document.createElement('a');
                    link.href = 'data:application/octet-stream;base64,' + base64;
                    link.download = data.fileName;
                    link.textContent = `下载文件: ${data.fileName}`;
                    link.style.display = 'block';
                    appendSystemMessage(link.outerHTML);
                    delete fileReceiveBuffer[key];
                }
            }
        }

        sendButton.onclick = sendMessage;
        messageInput.addEventListener('keypress', e => {
            if (e.key === 'Enter') sendMessage();
        });

        function sendMessage() {
            const content = messageInput.value.trim();
            if (!content) return;
            if (!selectedPeer) {
                alert('请先选择一个节点');
                return;
            }
            ws.send(JSON.stringify({
                type: 'message',
                targetUsername: selectedPeer.username,
                targetGroup: selectedPeer.group,
                content: content
            }));
            const key = peerKey(selectedPeer);
            saveChat(key, { username: myInfo.username, group: myInfo.group, content, self: true, time: new Date() });
            renderChatWithPeer(key);
            messageInput.value = '';
        }

        // 只显示与当前选中节点的消息
        function renderChatWithPeer(key) {
            messagesContainer.innerHTML = '';
            (chatHistory[key] || []).forEach(msg => {
                appendMessage(msg, false);
            });
        }

        // 保存消息到历史
        function saveChat(key, msg) {
            if (!chatHistory[key]) chatHistory[key] = [];
            chatHistory[key].push(msg);
        }

        // 修改appendMessage，支持只渲染不保存
        function appendMessage({ username, group, content, self, time }, save = true) {
            if (save && selectedPeer) saveChat(peerKey(selectedPeer), { username, group, content, self, time: time || new Date() });
            const msgDiv = document.createElement('div');
            msgDiv.className = 'message' + (self ? ' sent' : '');
            const showTime = time ? new Date(time).toLocaleTimeString() : '';
            msgDiv.innerHTML = `<div class=\"message-content\">${content}</div><div class=\"message-info\">${self ? '我' : username + '（' + group + '）'}${showTime ? ' · ' + showTime : ''}</div>`;
            messagesContainer.appendChild(msgDiv);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }
        function appendSystemMessage(content) {
            const msgDiv = document.createElement('div');
            msgDiv.className = 'message';
            msgDiv.innerHTML = `<div class="message-content" style="background:#f0f0f0;text-align:center;">${content}</div>`;
            messagesContainer.appendChild(msgDiv);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }

        // 增加文件选择按钮
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.style.display = 'none';
        const fileBtn = document.createElement('button');
        fileBtn.textContent = '选择文件';
        fileBtn.style.marginRight = '8px';
        document.querySelector('.input-container').insertBefore(fileBtn, messageInput);
        document.querySelector('.input-container').appendChild(fileInput);

        fileBtn.onclick = () => fileInput.click();
        fileInput.onchange = function() {
            if (!selectedPeer) {
                alert('请先选择一个节点');
                fileInput.value = '';
                return;
            }
            if (fileInput.files.length > 0) {
                fileToSend = fileInput.files[0];
                sendFile(fileToSend);
                fileInput.value = '';
            }
        };

        function sendFile(file) {
            const chunkSize = 32 * 1024; // 32KB
            const totalChunks = Math.ceil(file.size / chunkSize);
            let currentChunk = 0;
            fileReader = new FileReader();
            fileReader.onload = function(e) {
                const chunkData = e.target.result.split(',')[1]; // base64
                ws.send(JSON.stringify({
                    type: 'fileChunk',
                    targetUsername: selectedPeer.username,
                    targetGroup: selectedPeer.group,
                    fileName: file.name,
                    fileSize: file.size,
                    chunkIndex: currentChunk,
                    totalChunks: totalChunks,
                    data: chunkData
                }));
                currentChunk++;
                if (currentChunk < totalChunks) {
                    readNextChunk();
                } else {
                    appendSystemMessage(`文件已发送: ${file.name}`);
                }
            };
            function readNextChunk() {
                const start = currentChunk * chunkSize;
                const end = Math.min(file.size, start + chunkSize);
                const blob = file.slice(start, end);
                fileReader.readAsDataURL(blob);
            }
            readNextChunk();
        }

        // 接收文件分片
        ws && ws.addEventListener('message', (event) => {
            const data = JSON.parse(event.data);
            if (data.type === 'fileChunk') {
                // 用发送方用户名+组名+文件名唯一标识
                const key = data.fromUsername + '|' + data.fromGroup + '_' + data.fileName;
                if (!fileReceiveBuffer[key]) {
                    fileReceiveBuffer[key] = { chunks: [], total: data.totalChunks, size: data.fileSize };
                }
                fileReceiveBuffer[key].chunks[data.chunkIndex] = data.data;
                // 如果全部收到
                if (fileReceiveBuffer[key].chunks.filter(Boolean).length === data.totalChunks) {
                    const base64 = fileReceiveBuffer[key].chunks.join('');
                    const link = document.createElement('a');
                    link.href = 'data:application/octet-stream;base64,' + base64;
                    link.download = data.fileName;
                    link.textContent = `下载文件: ${data.fileName}`;
                    link.style.display = 'block';
                    appendSystemMessage(link.outerHTML);
                    delete fileReceiveBuffer[key];
                }
            }
        });
    </script>
</body>
</html>
